//
// Created by syj on 2023/10/10.
//

#ifndef HELLO_DDS_DDSMESSAGEPUBSUB_HPP
#define HELLO_DDS_DDSMESSAGEPUBSUB_HPP

#include <fastdds/dds/domain/DomainParticipant.hpp>
#include <fastdds/dds/domain/DomainParticipantFactory.hpp>
//#include <fastdds/dds/publisher/qos/PublisherQos.hpp>
#include <fastdds/dds/publisher/Publisher.hpp>
#include <fastdds/dds/subscriber/Subscriber.hpp>

#include "common/macros.h"

class DDSPublisher
{
public:
    static DDSPublisher *Instance() {
        static DDSPublisher instance;
        return &instance;
    }
private:
    DDSPublisher(){}
    DDSPublisher(const DDSPublisher &) = delete;
    DDSPublisher(DDSPublisher &&)=delete;
    DDSPublisher &operator=(const DDSPublisher &) = delete;
    DDSPublisher &operator=(DDSPublisher && )=delete;
public:
    virtual ~DDSPublisher()
    {
        if (participant_ != nullptr)
        {
            if (publisher_ != nullptr)
            {
                participant_->delete_publisher(publisher_);
            }

            eprosima::fastdds::dds::DomainParticipantFactory::get_instance()->delete_participant(participant_);
        }
    }
    bool init(std::string domainName,uint32_t domain=0,DDSTransportType transport=DDSTransportType::SHM)
    {
        /* 1. 发布初始化 */
        eprosima::fastdds::dds::DomainParticipantQos pqos;
        pqos.name(domainName);

//    if (profile.empty())
        {
            // TRANSPORT CONFIG
            // If it is set, not use default and set the transport
//        if (transport != DDSTransportType::DEFAULT || hops > 0 )
            {
                pqos.transport().use_builtin_transports = false;

                switch ( transport )
                {
                    case DDSTransportType::SHM:
                    {
                        auto shm_transport = std::make_shared<eprosima::fastdds::rtps::SharedMemTransportDescriptor>();
                        pqos.transport().user_transports.push_back(shm_transport);
                    }
                        break;
                    case DDSTransportType::UDPv4:
                    {
                        auto udp_transport = std::make_shared<eprosima::fastdds::rtps::UDPv4TransportDescriptor>();
                        pqos.transport().user_transports.push_back(udp_transport);
                    }
                        break;
                    case DDSTransportType::UDPv6:
                    {
                        auto udp_transport = std::make_shared<eprosima::fastdds::rtps::UDPv6TransportDescriptor>();
                        pqos.transport().user_transports.push_back(udp_transport);
                    }
                        break;
                    case DDSTransportType::DEFAULT:
                    default:
                    {
                        // mimick default transport selection
                        auto udp_transport = std::make_shared<eprosima::fastdds::rtps::UDPv4TransportDescriptor>();
                        pqos.transport().user_transports.push_back(udp_transport);
#ifdef SHM_TRANSPORT_BUILTIN
                        auto shm_transport = std::make_shared<eprosima::fastdds::rtps::SharedMemTransportDescriptor>();
                    pqos.transport().user_transports.push_back(shm_transport);
#endif // SHM_TRANSPORT_BUILTIN
                    }
                }

                /*if ( hops > 0 )
                {
                    for (auto& transportDescriptor : pqos.transport().user_transports)
                    {
                        SocketTransportDescriptor* pT = dynamic_cast<SocketTransportDescriptor*>(transportDescriptor.get());
                        if (pT)
                        {
                            pT->TTL = (uint8_t)std::min(hops, 255);
                        }
                    }
                }*/
            }

            // CREATE THE PARTICIPANT
            participant_ = eprosima::fastdds::dds::DomainParticipantFactory::get_instance()->create_participant(domain, pqos);
        }
//    else
        {
            // Create participant from xml profile
//        participant_ = DomainParticipantFactory::get_instance()->create_participant_with_profile(profile);
        }

        if (participant_ == nullptr)
        {
            return false;
        }

        // CREATE THE PUBLISHER
        eprosima::fastdds::dds::PublisherQos pbqos;

        /*if (!partitions.empty())
        {
            // Divide in partitions by ;
            std::stringstream spartitions(partitions);
            std::string partition_cut;
            while (std::getline(spartitions, partition_cut, ';'))
            {
                pbqos.partition().push_back(partition_cut.c_str());
            }
        }*/

        // Create publisher
        publisher_ = participant_->create_publisher(pbqos, nullptr);

        if (publisher_ == nullptr)
        {
            return false;
        }

        // CREATE THE SUBSCRIBER
        eprosima::fastdds::dds::SubscriberQos sqos;

        /*if (!partitions.empty())
        {
            // Divide in partitions by ;
            std::stringstream spartitions(partitions);
            std::string partition_cut;
            while (std::getline(spartitions, partition_cut, ';'))
            {
                sqos.partition().push_back(partition_cut.c_str());
            }
        }*/

        subscriber_ = participant_->create_subscriber(sqos, nullptr);

        if (subscriber_ == nullptr)
        {
            return false;
        }

        return true;
    }

    eprosima::fastdds::dds::DomainParticipant* GetParticipant() const
    {
        return participant_;
    }
    eprosima::fastdds::dds::Publisher* GetPublisher() const
    {
        return publisher_;
    }
    eprosima::fastdds::dds::Subscriber* GetSubscriber() const
    {
        return subscriber_;
    }
private:
    eprosima::fastdds::dds::DomainParticipant* participant_;

    eprosima::fastdds::dds::Publisher* publisher_;
    eprosima::fastdds::dds::Subscriber* subscriber_;
};

#endif //HELLO_DDS_DDSMESSAGEPUBLISHER_HPP
